route.js 1.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
  1. import { listYears } from "@/lib/storage";
  2. import { getSession } from "@/lib/auth/session";
  3. import { canAccessBranch } from "@/lib/auth/permissions";
  4. import {
  5. withErrorHandling,
  6. json,
  7. badRequest,
  8. unauthorized,
  9. forbidden,
  10. } from "@/lib/api/errors";
  11. import { mapStorageReadError } from "@/lib/api/storageErrors";
  12. /**
  13. * GET /api/branches/[branch]/years
  14. *
  15. * Happy-path response must remain unchanged:
  16. * { "branch": "NL01", "years": ["2024", ...] }
  17. */
  18. export const GET = withErrorHandling(
  19. async function GET(request, ctx) {
  20. const session = await getSession();
  21. if (!session) {
  22. throw unauthorized("AUTH_UNAUTHENTICATED", "Unauthorized");
  23. }
  24. // Next.js App Router: params are resolved asynchronously.
  25. const { branch } = await ctx.params;
  26. // Validate required route params early (client error => 400).
  27. if (!branch) {
  28. throw badRequest(
  29. "VALIDATION_MISSING_PARAM",
  30. "Missing required route parameter(s)",
  31. { params: ["branch"] }
  32. );
  33. }
  34. // RBAC: branch users can only access their own branch.
  35. if (!canAccessBranch(session, branch)) {
  36. throw forbidden("AUTH_FORBIDDEN_BRANCH", "Forbidden");
  37. }
  38. try {
  39. const years = await listYears(branch);
  40. return json({ branch, years }, 200);
  41. } catch (err) {
  42. // Convert filesystem errors into:
  43. // - 404 if the requested path does not exist (but NAS root is reachable)
  44. // - 500 for system-level storage failures
  45. throw await mapStorageReadError(err, { details: { branch } });
  46. }
  47. },
  48. { logPrefix: "[api/branches/[branch]/years]" }
  49. );